package com.logic.landseaserver.models.payment.service;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.logic.common.security.SecurityUtils;
import com.logic.landseaserver.common.enums.BTypeEnum;
import com.logic.landseaserver.common.enums.BillStatusEnum;
import com.logic.landseaserver.common.enums.BillTypeEnum;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.exception.ResultCodes.PaymentResultCode;
import com.logic.landseaserver.common.util.Arith;
import com.logic.landseaserver.common.util.LandSeaUtil;
import com.logic.landseaserver.domain.Bill;
import com.logic.landseaserver.domain.BillDetail;
import com.logic.landseaserver.domain.BillPayment;
import com.logic.landseaserver.domain.SyncBillPayment;
import com.logic.landseaserver.models.payment.BillPaymentCollections;
import com.logic.landseaserver.models.payment.PaymentConstant;
import com.logic.landseaserver.models.payment.PaymentConstant.BillPaymentStatus;
import com.logic.landseaserver.models.payment.bean.PaymentQueryRequest;
import com.logic.landseaserver.models.payment.bean.PaymentQueryResult;
import com.logic.landseaserver.models.payment.bean.PaymentRequest;
import com.logic.landseaserver.models.payment.channel.AbstractPaymentChannel;
import com.logic.landseaserver.models.prepayment.bean.AotuPayBill;
import com.logic.landseaserver.persistence.read.BillDetailReadMapper;
import com.logic.landseaserver.persistence.read.BillPaymentReadMapper;
import com.logic.landseaserver.persistence.read.BillReadMapper;
import com.logic.landseaserver.persistence.write.BillDetailWriteMapper;
import com.logic.landseaserver.persistence.write.BillWriteMapper;
import com.logic.landseaserver.ws.dto.BillDTO;
import com.logic.system.AppContext;

/**
 * 
 *
 * 项目名称：system-server</br>
 * 类名称：PaymentService</br>
 * 类描述：支付服务入口</br>
 * 创建人：Aaron</br>
 * 创建时间：2017年7月17日 下午4:29:22</br>
 * 
 * @version 1.0
 *
 */
@Service
public class PaymentService
{
    protected static final Logger LOGGER = LoggerFactory.getLogger(PaymentService.class);
    
    @Autowired
    private AppContext appContext;
    
    @Autowired
    private BillPaymentReadMapper billPaymentReadMapper;
    
    @Autowired
    private BillReadMapper billReadMapper;
    
    @Autowired
    private BillWriteMapper billWriteMapper;
    
    @Autowired
    private BillDetailReadMapper billDetailReadMapper;
    
    @Autowired
    private BillDetailWriteMapper billDetailWriteMapper;
    
    /**
     * 
     * [简要描述]：支付</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @param paymentRequest
     * @return
     * @throws LandseaException
     *
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public Map<String, Object> pay(PaymentRequest paymentRequest)
        throws LandseaException
    {
        String billType = paymentRequest.getBillType();
        if (StringUtils.isEmpty(billType))
        {
            throw LandseaException.createException(PaymentResultCode.E00140005);
        }
        
        String processBeanId = billType + PaymentConstant.PAYMENT_REQUEST_SUFFIX;
        
        // 根据账单类型路由处理组件，路由不到报错
        Object handerObject = appContext.getBean(processBeanId);
        if (null == handerObject)
        {
            throw LandseaException.createException(PaymentResultCode.E00140005);
        }
        
        AbstractPaymentRequestHandler paymentRequestHandler = (AbstractPaymentRequestHandler)handerObject;
        
        // 开始处理
        Map<String, Object> resultMap = paymentRequestHandler.doPay(paymentRequest);
        return resultMap;
    }
    
    /**
     * 
     * [简要描述]：支付结果查询</br>
     * [详细描述]：如果是未支付要请求第三方</br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @param paymentRequest
     * @return
     * @throws LandseaException
     *
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public Map<String, Object> paymentQuery(PaymentQueryRequest paymentQuery)
        throws LandseaException
    {
        LOGGER.info("paymentQuery|" + paymentQuery);
        
        Map<String, Object> resultMap = new HashMap<String, Object>();
        
        Integer paymentId = paymentQuery.getPaymentId();
        
        if (null == paymentId)
        {
            throw LandseaException.createException(PaymentResultCode.E00140007);
        }
        
        try
        {
            // 查询支付记录
            BillPayment billPayment = billPaymentReadMapper.selectByPrimaryKey(paymentId);
            if (null == billPayment)
            {
                throw LandseaException.createException(PaymentResultCode.E00140008);
            }
            
            // 如果不是支付成功，则查询
            if (BillPaymentStatus.SUCCESS.equals(billPayment.getStatus()))
            {
                // 支付结果
                resultMap.put("paymentStatus", billPayment.getStatus());
                LOGGER.info("paymentQuery|payment is finished.paymentId=" + billPayment.getId());
                return resultMap;
            }
            
            // 根据支付渠道查询第三方订单
            String beanId = PaymentConstant.GENETRATE_BILLPAYMENT_PREFIX
                + BillPaymentCollections.getPayChannelByPayType(billPayment.getPayType());
            AbstractPaymentChannel paymentChannel = (AbstractPaymentChannel)appContext.getBean(beanId);
            
            if (null == paymentChannel)
            {
                LOGGER.error("paymentQuery|get paymentChannel failed,beanId=" + beanId);
                throw LandseaException.createException(PaymentResultCode.E00150006);
            }
            
            PaymentQueryResult paymentQueryResult = new PaymentQueryResult();
            paymentChannel.paymentQuery(paymentQuery, billPayment, paymentQueryResult, resultMap);
            
            // 是否需要查询后的处理
            if (!paymentQueryResult.isNeedQueryProcess())
            {
                return resultMap;
            }
            
            // 处理回调
            Map<String, Object> payCallbackResultMap =
                payCallback(String.valueOf(paymentId), paymentQueryResult.getQueryResponseResult());
            
            resultMap.putAll(payCallbackResultMap);
            
            return resultMap;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("PaymentService|payCallback|query billpayment failed. billPaymentId=" + paymentId, e);
            throw LandseaException.createException(PaymentResultCode.E00150004);
        }
    }
    
    /**
     * 
     * [简要描述]：支付结果通知处理</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @param paymentId
     * @param paymentResponse
     * @return
     * @throws LandseaException
     *
     */
    public synchronized Map<String, Object> payCallback(String paymentId, Object paymentResponse)
        throws LandseaException
    {
        LOGGER.info("PaymentService|payCallback|start process|paymentId=" + paymentId);
        
        if (StringUtils.isEmpty(paymentId))
        {
            throw LandseaException.createException(PaymentResultCode.E00140007);
        }
        
        try
        {
            Integer paymentIdInt = Integer.parseInt(paymentId);
            
            // 查询支付记录
            BillPayment billPayment = billPaymentReadMapper.selectByPrimaryKey(paymentIdInt);
            if (null == billPayment)
            {
                throw LandseaException.createException(PaymentResultCode.E00140008);
            }
            
            // 已经支付成功了，就直接返回
            if (BillPaymentStatus.SUCCESS.equals(billPayment.getStatus()))
            {
                // 支付结果
                LOGGER.info("payCallback|支付已经成功，消息不处理.paymentId=" + billPayment.getId());
                Map<String, Object> resultMap = new HashMap<String, Object>();
                resultMap.put("status", 0);
                resultMap.put("msg", "success");
                return resultMap;
            }
            
            String billType = billPayment.getBillType();
            String processBeanId = billType + PaymentConstant.PAYMENT_RESPONSE_SUFFIX;
            
            // 根据账单类型路由处理组件，路由不到报错
            Object handerObject = appContext.getBean(processBeanId);
            if (null == handerObject)
            {
                throw LandseaException.createException(PaymentResultCode.E00150005);
            }
            
            AbstractPaymentResponseHandler paymentRequestHandler = (AbstractPaymentResponseHandler)handerObject;
            
            // 开始处理回调
            Map<String, Object> resultMap = paymentRequestHandler.doResponse(billPayment, paymentResponse);
            
            // 回调处理完毕，提交事务后处理
            paymentRequestHandler.afterRespCommitTranscationProcess(resultMap);
            
            LOGGER.info("PaymentService|payCallback|Process finished - paymentId=" + paymentId);
            
            return resultMap;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("PaymentService|payCallback|query billpayment failed. billPaymentId=" + paymentId, e);
            throw LandseaException.createException(PaymentResultCode.E00150004);
        }
    }
    
    /**
     * 
     * [简要描述]：更新bill表的状态及金额及账单号</br>
     * [详细描述]：调用者处理异常,给直接支付账单使用 </br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @param billId
     * @param payMoney
     * @throws LandseaException
     *
     */
    public BillDTO updateBillStateAndMoney(Integer billId, Double payMoney)
        throws LandseaException
    {
        try
        {
            String billStatus = StringUtils.EMPTY;
            
            // 获取账单信息
            BillDTO bill = billReadMapper.selectByPrimaryKey(billId);
            
            if (null == bill)
            {
                throw LandseaException.createException(PaymentResultCode.E00150002);
            }
            
            // 应收金额
            Double receivable = bill.getReceivableMoney();
            
            // 已收金额
            Double amountReceived = bill.getAmountReceived();
            
            // 判断 应收金额 和 已收金额 + 当前支付金额 大小
            
            // 部分支付 BIST1 已支付 BIST2 未支付 BIST3
            Double sumPayMoney = Arith.add(amountReceived, payMoney);
            
            Bill billUpdate = new Bill();
            
            // 未支付
            if (sumPayMoney.doubleValue() == 0.00d)
            {
                billStatus = BillStatusEnum.BIST3.getCode();
            }
            // 应收金额 <= 已收金额 + 当前支付金额
            else if (receivable.doubleValue() > sumPayMoney.doubleValue())
            {
                billStatus = BillStatusEnum.BIST1.getCode();
            }
            else if (receivable.doubleValue() == sumPayMoney.doubleValue())
            {
                billStatus = BillStatusEnum.BIST2.getCode();
            }
            else
            {
                // 总支付金额不能大于应收金额
                LOGGER.error(
                    "updateBillStateAndMoney|Total pay money can not more than receivable money. billId=" + billId
                        + ", payMoney=" + payMoney + ",received=" + amountReceived + ",receivableMoney=" + receivable);
                throw LandseaException.createException(PaymentResultCode.E00150009);
            }
            
            billUpdate.setId(billId);
            billUpdate.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
            billUpdate.setBillStatus(billStatus);
            billUpdate.setAmountReceived(sumPayMoney);
            // 此处的创建时间一定不能改，bill表中的createedDtm计算滞纳金要用
            billUpdate.setCreatedDtm(null);
            billUpdate.setCreatedBy(null);
            // 只要支付了，就设置该字段为1，如果为1，那么不能使用授权码
            billUpdate.setPayWithMoney(1);
            billWriteMapper.updateByPrimaryKeySelective(billUpdate);
            
            bill.setBillStatus(billStatus);
            bill.setAmountReceived(sumPayMoney);
            
            // 更新详情表每项的已支付,查询出来的已经是按实际支付金额从小到大排列的,去除已经支付完毕的
            List<BillDetail> detailList = billDetailReadMapper.selectAutoPayUpdateList(billId);
            if (CollectionUtils.isEmpty(detailList))
            {
                return bill;
            }
            
            payMoney = LandSeaUtil.get2Double(payMoney);
            
            // 处理授权码减免、滞纳金
            Map<String,Double> couponMap = processCouponAndLateFee(detailList);
            
            BillDetail updateBillDetail = null;
            for (BillDetail billDetail : detailList)
            {
                if (payMoney <= 0.00d)
                {
                    break;
                }
                
                // 如果收授权码减免，先减去钱
                Double couponMoney = couponMap.get(billDetail.getType());
                if (null != couponMoney)
                {
                    billDetail.setMoney(Arith.add(billDetail.getMoney(), couponMoney.doubleValue()));
                }
                
                // 账单还需支付金额
                double billMoney = Arith.sub(billDetail.getMoney(), billDetail.getPaidMoney());
                double updateMoney = 0.00d;
                if (payMoney >= billMoney)
                {
                    updateMoney = billDetail.getMoney();
                    payMoney = Arith.sub(payMoney, billMoney);
                }
                else
                {
                    updateMoney = Arith.add(billDetail.getPaidMoney(), payMoney);
                    payMoney = 0.00d;
                }
                updateBillDetail = new BillDetail();
                updateBillDetail.setId(billDetail.getId());
                updateBillDetail.setPaidMoney(updateMoney);
                billDetailWriteMapper.updateByPrimaryKeySelective(updateBillDetail);
                LOGGER.info("updateBillStateAndMoney|更改ID=" + billDetail.getId() + "的已支付金额" + billDetail.getPaidMoney()
                    + "为" + updateMoney);
            }
            
            return bill;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("updateBillStateAndMoney|update ls_bill failed. billId=" + billId, e);
            throw LandseaException.createException(PaymentResultCode.E00150010);
        }
    }
    
    /**
     * 
     * [简要描述]：更新bill表的状态及金额及账单号</br>
     * [详细描述]：调用者处理异常,给充值自动抵扣用 </br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @throws LandseaException
     *
     */
    public BillDTO updateBillStateAndMoney4AutoPay(AotuPayBill aotuPayBill, BillPayment payment)
        throws LandseaException
    {
        Integer billId = payment.getBillId();
        Double payMoney = payment.getMoney();
        try
        {
            String billStatus = StringUtils.EMPTY;
            
            // 获取账单信息
            BillDTO bill = billReadMapper.selectByPrimaryKey(billId);
            
            if (null == bill)
            {
                throw LandseaException.createException(PaymentResultCode.E00150002);
            }
            
            // 应收金额
            Double receivable = bill.getReceivableMoney();
            
            // 已收金额
            Double amountReceived = bill.getAmountReceived();
            
            // 判断 应收金额 和 已收金额 + 当前支付金额 大小
            
            // 部分支付 BIST1 已支付 BIST2 未支付 BIST3
            Double sumPayMoney = Arith.add(amountReceived, payMoney);
            
            Bill billUpdate = new Bill();
            
            // 未支付
            if (sumPayMoney.doubleValue() == 0.00d)
            {
                billStatus = BillStatusEnum.BIST3.getCode();
            }
            // 应收金额 <= 已收金额 + 当前支付金额
            else if (receivable.doubleValue() > sumPayMoney.doubleValue())
            {
                billStatus = BillStatusEnum.BIST1.getCode();
            }
            else if (receivable.doubleValue() == sumPayMoney.doubleValue())
            {
                billStatus = BillStatusEnum.BIST2.getCode();
            }
            else
            {
                // 总支付金额不能大于应收金额
                LOGGER.error(
                    "updateBillStateAndMoney|Total pay money can not more than receivable money. billId=" + billId
                        + ", payMoney=" + payMoney + ",received=" + amountReceived + ",receivableMoney=" + receivable);
                throw LandseaException.createException(PaymentResultCode.E00150009);
            }
            
            billUpdate.setId(billId);
            billUpdate.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
            billUpdate.setBillStatus(billStatus);
            billUpdate.setAmountReceived(sumPayMoney);
            // 此处的创建时间一定不能改，bill表中的createedDtm计算滞纳金要用
            billUpdate.setCreatedDtm(null);
            billUpdate.setCreatedBy(null);
            billWriteMapper.updateByPrimaryKeySelective(billUpdate);
            
            bill.setBillStatus(billStatus);
            bill.setAmountReceived(sumPayMoney);
            
            // 更新详情表已支付
            BillDetail updateBillDetail = new BillDetail();
            updateBillDetail.setId(aotuPayBill.getBillDetailId());
            updateBillDetail.setPaidMoney(aotuPayBill.getPaidDetailMoney());
            billDetailWriteMapper.updateByPrimaryKeySelective(updateBillDetail);
            LOGGER.info("updateBillStateAndMoney4AutoPay|更改ID=" + aotuPayBill.getBillDetailId() + "的已支付金额" + aotuPayBill.getSourcePaidDetailMoney()
                + "为" + aotuPayBill.getPaidDetailMoney());
            
            return bill;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("updateBillStateAndMoney|update ls_bill failed. billId=" + billId, e);
            throw LandseaException.createException(PaymentResultCode.E00150010);
        }
    }    
    
    /**
     * 
     * [简要描述]：生成合同编号</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @param prjCode
     * @param maxContractCode
     * @return
     *
     */
    public String generateContractNo(String prjCode, Integer maxContractCode)
    {
        String contractSn = prjCode + new DecimalFormat("000000").format(maxContractCode);
        return contractSn;
    }
    
    public void syncBillPayment()
    {
        try
        {
            LOGGER.info("支付账单同步开始");
            Map<String, Object> queryMap = new HashMap<String, Object>();
            
            Calendar startCalendar = Calendar.getInstance();
            startCalendar.set(Calendar.HOUR_OF_DAY, 0);
            startCalendar.set(Calendar.MINUTE, 0);
            startCalendar.set(Calendar.SECOND, 0);
            Date start = startCalendar.getTime();
            
            Calendar endCalendar = Calendar.getInstance();
            endCalendar.add(Calendar.MINUTE, -15);
            Date end = endCalendar.getTime();
            
            queryMap.put("startTime", start);
            queryMap.put("endTime", end);
            
            List<SyncBillPayment> list = billPaymentReadMapper.noSuccessPaymentBill(queryMap);
            LOGGER.info("未支付账单=>" + list);
            if (CollectionUtils.isEmpty(list))
            {
                return;
            }
            
            for (SyncBillPayment syncBillPayment : list)
            {
                try
                {
                    PaymentQueryRequest paymentQuery = new PaymentQueryRequest();
                    if (!BTypeEnum._BT4.getCode().equals(syncBillPayment.getBillType()))
                    {
                        paymentQuery.setContractId(syncBillPayment.getContractId());
                    }
                    paymentQuery.setPaymentId(syncBillPayment.getPaymentId());
                    paymentQuery.setProjectId(syncBillPayment.getProjectId());
                    
                    paymentQuery(paymentQuery);
                }
                catch (LandseaException e1)
                {
                    LOGGER.error("syncNoSuccessPaymentBill|处理" + syncBillPayment + "失败", e1);
                }
            }
            
            LOGGER.info("支付账单同步结束");
        }
        catch (Exception e)
        {
            LOGGER.error("syncNoSuccessPaymentBill Failed.", e);
        }
    }
    
    /**
     * 
     * [简要描述]：续租结转处理</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-08)</br>
     *
     * @param payMoney
     * @param detailList
     *
     */
    public void reRentFirstBillPaidProcess(double payMoney, List<BillDetail> detailList)
    {
        // 更新详情表每项的已支付,查询出来的已经是按实际支付金额从小到大排列的,去除已经支付完毕的
        payMoney = LandSeaUtil.get2Double(payMoney);
        
        BillDetail updateBillDetail = null;
        for (BillDetail billDetail : detailList)
        {
            if (payMoney <= 0.00d)
            {
                break;
            }
            
            // 首期账单首次生成，不可能有支付
            billDetail.setPaidMoney(0.00d);
            
            double minMoney = Arith.sub(billDetail.getMoney(), billDetail.getPaidMoney());
            double updateMoney = 0.00d;
            if (payMoney >= minMoney)
            {
                updateMoney = billDetail.getMoney();
                payMoney = Arith.sub(payMoney, minMoney);
            }
            else
            {
                updateMoney = Arith.add(billDetail.getPaidMoney(), payMoney);
                payMoney = 0.00d;
            }
            updateBillDetail = new BillDetail();
            updateBillDetail.setId(billDetail.getId());
            updateBillDetail.setPaidMoney(updateMoney);
            billDetailWriteMapper.updateByPrimaryKeySelective(updateBillDetail);
            LOGGER.info("reRentFirstBillPaidProcess|更改ID=" + billDetail.getId() + "的已支付金额" + billDetail.getPaidMoney()
                + "为" + updateMoney);
        }
    }
    
    /**
     * 
     * [简要描述]：处理授权码减免以及滞纳金</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2018-01-12)</br>
     *
     * @param autoPaybillList
     *
     */
    private Map<String,Double> processCouponAndLateFee(List<BillDetail> detailList)
    {
        Map<String,Double> couponMap = new HashMap<String,Double>();
        if (CollectionUtils.isEmpty(detailList))
        {
            return couponMap;
        }
        LOGGER.info("processCouponAndLateFee|处理授权码减免，去除授权码金额，将滞纳金放在最后抵扣");
        Iterator<BillDetail> iterator = detailList.iterator();
        BillDetail aotuPayBill = null;
        
        List<BillDetail> lateFeeList = new ArrayList<BillDetail>();
        
        while (iterator.hasNext())
        {
            aotuPayBill = iterator.next();
            if (aotuPayBill.getMoney() < 0)
            {
                couponMap.put(aotuPayBill.getType(), aotuPayBill.getMoney());
                iterator.remove();
                continue;
            }
            
            if (BillTypeEnum.CTBI13.getCode().equals(aotuPayBill.getType()))
            {
                lateFeeList.add(aotuPayBill);
                iterator.remove();
                continue;
            }
        }
        
        // 将滞纳金添加进去放在最后
        for (BillDetail lateFeeBill : lateFeeList)
        {
            detailList.add(lateFeeBill);
        }
        
        return couponMap;
    }
}
